package org.jeecg.modules.device.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.jarslink.api.Module;
import com.alipay.jarslink.api.ModuleConfig;
import com.alipay.jarslink.api.ModuleLoader;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhouwr.common.device.vo.function.FunctionParamVo;
import com.zhouwr.common.device.vo.function.ModelFunctionVo;
import com.zhouwr.common.exception.IotConfigException;
import com.zhouwr.common.network.DeviceMessageCodec;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.device.entity.*;
import org.jeecg.modules.device.mapper.*;
import org.jeecg.modules.device.service.IDeviceModelService;
import org.jeecg.modules.device.vo.label.ViewDataVo;
import org.jeecg.modules.protocol.entity.ProtocolData;
import org.jeecg.modules.protocol.mapper.ProtocolDataMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 设备模型服务impl
 *
 * @author zhouwenrong
 * @Description: 设备模型
 * @Version: V1.0
 * @date 2022/01/06
 */
@Slf4j
@Service
public class DeviceModelServiceImpl extends ServiceImpl<DeviceModelMapper, DeviceModel> implements IDeviceModelService {

    /**
     * 设备模型映射器
     */
    @Autowired
    private DeviceModelMapper deviceModelMapper;
    /**
     * 设备映射器实例
     */
    @Autowired
    private DeviceInstanceMapper deviceInstanceMapper;
    /**
     * 设备数据映射器
     */
    @Autowired
    private DeviceDataMapper deviceDataMapper;
    /**
     * 设备功能映射器
     */
    @Autowired
    private DeviceFunctionMapper deviceFunctionsMapper;
    /**
     * 设备事件映射器
     */
    @Autowired
    private DeviceEventMapper deviceEventMapper;
    /**
     * 设备标签映射器
     */
    @Autowired
    private DeviceLabelMapper deviceLabelMapper;
    /**
     * 协议数据映射器
     */
    @Autowired
    private ProtocolDataMapper protocolDataMapper;
    /**
     * 出版商
     */
    @Autowired
    private ApplicationEventPublisher publisher;
    /**
     * 模块加载器
     */
    @Resource
    private ModuleLoader moduleLoader;

    /**
     * 上传路径
     */
    @Value("${jeecg.path.upload}")
    private String uploadPath;

    /**
     * 救主
     *
     * @param model              模型
     * @param deviceDataList     设备数据表
     * @param deviceFunctionList 设备功能列表
     * @param deviceEventList    设备事件列表
     * @param deviceLabelList    设备标签列表
     */
    @Override
    @Transactional
    public void saveMain(DeviceModel model, List<DeviceData> deviceDataList, List<DeviceFunction> deviceFunctionList, List<DeviceEvent> deviceEventList, List<DeviceLabel> deviceLabelList) {
        deviceModelMapper.insert(model);
        if (deviceDataList != null && deviceDataList.size() > 0) {
            for (DeviceData entity : deviceDataList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceDataMapper.insert(entity);
            }
        }
        if (deviceFunctionList != null && deviceFunctionList.size() > 0) {
            for (DeviceFunction entity : deviceFunctionList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceFunctionsMapper.insert(entity);
            }
        }
        if (deviceEventList != null && deviceEventList.size() > 0) {
            for (DeviceEvent entity : deviceEventList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceEventMapper.insert(entity);
            }
        }
        if (deviceLabelList != null && deviceLabelList.size() > 0) {
            for (DeviceLabel entity : deviceLabelList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceLabelMapper.insert(entity);
            }
        }
    }

    /**
     * 更新主要
     *
     * @param model              模型
     * @param deviceDataList     设备数据表
     * @param deviceFunctionList 设备功能列表
     * @param deviceEventList    设备事件列表
     * @param deviceLabelList    设备标签列表
     */
    @Override
    @Transactional
    public void updateMain(DeviceModel model, List<DeviceData> deviceDataList, List<DeviceFunction> deviceFunctionList, List<DeviceEvent> deviceEventList, List<DeviceLabel> deviceLabelList) {
        deviceModelMapper.updateById(model);

        //1.先删除子表数据
        deviceDataMapper.deleteByMainId(model.getId());
        deviceFunctionsMapper.deleteByMainId(model.getId());
        deviceEventMapper.deleteByMainId(model.getId());
        deviceLabelMapper.deleteByMainId(model.getId());

        //2.子表数据重新插入
        if (deviceDataList != null && deviceDataList.size() > 0) {
            for (DeviceData entity : deviceDataList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceDataMapper.insert(entity);
            }
        }
        if (deviceFunctionList != null && deviceFunctionList.size() > 0) {
            for (DeviceFunction entity : deviceFunctionList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceFunctionsMapper.insert(entity);
            }
        }
        if (deviceEventList != null && deviceEventList.size() > 0) {
            for (DeviceEvent entity : deviceEventList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceEventMapper.insert(entity);
            }
        }
        if (deviceLabelList != null && deviceLabelList.size() > 0) {
            for (DeviceLabel entity : deviceLabelList) {
                //外键设置
                entity.setDeviceModelBy(model.getId());
                deviceLabelMapper.insert(entity);
            }
        }
    }

    /**
     * 德尔主要
     *
     * @param id id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delMain(String id) {
        deviceDataMapper.deleteByMainId(id);
        deviceFunctionsMapper.deleteByMainId(id);
        deviceEventMapper.deleteByMainId(id);
        deviceLabelMapper.deleteByMainId(id);
        deviceModelMapper.deleteById(id);
    }

    /**
     * 德尔批主要
     *
     * @param idList id列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delBatchMain(Collection<? extends Serializable> idList) {
        for (Serializable id : idList) {
            deviceDataMapper.deleteByMainId(id.toString());
            deviceFunctionsMapper.deleteByMainId(id.toString());
            deviceEventMapper.deleteByMainId(id.toString());
            deviceLabelMapper.deleteByMainId(id.toString());
            deviceModelMapper.deleteById(id);
        }
    }

    /**
     * 获得协议
     *
     * @param modelId 模型id
     * @return {@link DeviceMessageCodec}
     */
    @Override
    public DeviceMessageCodec getProtocol(String modelId) {
        final DeviceModel model = getById(modelId);
        final ProtocolData protocolData = protocolDataMapper.selectOne(
                new LambdaQueryWrapper<ProtocolData>()
                        .eq(ProtocolData::getId, model.getDataProtocolBy())
                        .eq(ProtocolData::getState, 1)
        );
        if (protocolData != null) {
            return this.loadPackage(protocolData.getPackagePath(), protocolData.getClassPath());
        } else {
            String msg = "设备模型【" + model.getName() + "】获取编解码失败，请检测数据协议配置！";
            throw new IotConfigException(msg);
        }
    }

    /**
     * 克隆
     *
     * @param id id
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void clone(String id) {
        // 根据id获取模型
        final DeviceModel model = this.getById(id);
        // 克隆模型
        DeviceModel cloneModel = null;
        try {
            cloneModel = (DeviceModel) BeanUtils.cloneBean(model);
        } catch (Exception e) {
            e.printStackTrace();
        }
        assert cloneModel != null;
        cloneModel.setId(null);
        cloneModel.setCode(model.getCode() + "-clone");
        cloneModel.setName(model.getName() + "-克隆");
        cloneModel.setCreateTime(new Date());

        // 数据节点
        List<DeviceData> deviceDatas = deviceDataMapper.selectByMainId(id)
                .stream()
                .map(deviceData -> {
                    DeviceData cloneData = null;
                    try {
                        cloneData = (DeviceData) BeanUtils.cloneBean(deviceData);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    assert cloneData != null;
                    cloneData.setId(null);
                    cloneData.setCode(deviceData.getCode() + "-clone");
                    cloneData.setName(deviceData.getName() + "-克隆");
                    return cloneData;
                })
                .collect(Collectors.toList());

        // 功能
        List<DeviceFunction> deviceFunctions = deviceFunctionsMapper.selectByMainId(id)
                .stream()
                .map(function -> {
                    DeviceFunction cloneFunction = null;
                    try {
                        cloneFunction = (DeviceFunction) BeanUtils.cloneBean(function);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    assert cloneFunction != null;
                    cloneFunction.setId(null);
                    cloneFunction.setCode(function.getCode() + "-clone");
                    cloneFunction.setName(function.getName() + "-克隆");
                    return cloneFunction;
                })
                .collect(Collectors.toList());

        // 事件
        List<DeviceEvent> deviceEvents = deviceEventMapper.selectByMainId(id)
                .stream()
                .map(event -> {
                    DeviceEvent cloneEvent = null;
                    try {
                        cloneEvent = (DeviceEvent) BeanUtils.cloneBean(event);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    assert cloneEvent != null;
                    cloneEvent.setId(null);
                    cloneEvent.setCode(event.getCode() + "-clone");
                    cloneEvent.setName(event.getName() + "-克隆");
                    return cloneEvent;
                })
                .collect(Collectors.toList());
        // 标签
        List<DeviceLabel> deviceLabels = deviceLabelMapper.selectByMainId(id)
                .stream()
                .map(label -> {
                    DeviceLabel cloneLabel = null;
                    try {
                        cloneLabel = (DeviceLabel) BeanUtils.cloneBean(label);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    assert cloneLabel != null;
                    cloneLabel.setId(null);
                    cloneLabel.setCode(label.getCode() + "-clone");
                    cloneLabel.setTitle(label.getTitle() + "-克隆");
                    return cloneLabel;
                })
                .collect(Collectors.toList());
        this.saveMain(cloneModel, deviceDatas, deviceFunctions, deviceEvents, deviceLabels);
    }

    /**
     * 加载包
     *
     * @param packagePath 包路径
     * @param classPath   类路径
     * @return {@link DeviceMessageCodec}
     */
    public DeviceMessageCodec loadPackage(String packagePath, String classPath) {
        Object o = null;
        try {
            /* jar包的路径 */
            File file = new File(uploadPath + "/" + packagePath);
            log.info("编解码文件：{}", file);
            URL url = file.toURI().toURL();
            /* 创建类加载器 */
            ClassLoader loader = new URLClassLoader(new URL[]{url});
            /* 加载指定类，注意一定要带上类的包名 */
            Class<?> cls = loader.loadClass(classPath);
            o = cls.newInstance();
            log.info("编解码器：{}", JSONObject.toJSONString(o));
        } catch (Exception e) {
            e.printStackTrace();
        }
        /* 初始化一个实例 */
        return (DeviceMessageCodec) o;
    }

    /**
     * 加载包
     *
     * @param jarUrl jar url
     * @return {@link DeviceMessageCodec}
     */
    public DeviceMessageCodec loadPackage(String jarUrl) {
        URL modelUrl = Thread.currentThread().getContextClassLoader().getResource(jarUrl);
        ModuleConfig moduleConfig = new ModuleConfig();
        moduleConfig.addModuleUrl(modelUrl);
        Module module = moduleLoader.load(moduleConfig);
        return module.doAction("", null);
    }

    /**
     * 通过id获取属性
     *
     * @param modelId    模型id
     * @param viewDataVo 显示数据vo {@link ViewDataVo}
     * @return {@link String}
     */
    @Override
    public String getAttributeById(String modelId, ViewDataVo viewDataVo) {
        String type = viewDataVo.getType();
        String column = oConvertUtils.camelToUnderline(viewDataVo.getKey());
        String dictTable = StringUtils.isNotBlank(viewDataVo.getConvert().getDictTable()) ? oConvertUtils.camelToUnderline(viewDataVo.getConvert().getDictTable()) : "";
        String dictKey = StringUtils.isNotBlank(viewDataVo.getConvert().getDictKey()) ? oConvertUtils.camelToUnderline(viewDataVo.getConvert().getDictKey()) : "";
        String dictValue = StringUtils.isNotBlank(viewDataVo.getConvert().getDictValue()) ? oConvertUtils.camelToUnderline(viewDataVo.getConvert().getDictValue()) : "";
        String dateformat = viewDataVo.getDateformat();
        if ("database".equals(viewDataVo.getType())) {
            if (StringUtils.isBlank(dictTable)) {
                throw new RuntimeException(viewDataVo.getName() + " dictTable is null");
            } else {
                if (StringUtils.isBlank(dictKey)) {
                    throw new RuntimeException(viewDataVo.getName() + " dictKey is null");
                }
                if (StringUtils.isBlank(dictValue)) {
                    throw new RuntimeException(viewDataVo.getName() + " dictValue is null");
                }
            }
        } else if ("dateformat".equals(viewDataVo.getType())) {
            if (StringUtils.isBlank(dateformat)) {
                throw new RuntimeException(viewDataVo.getName() + " dateformat is null");
            }
        } else if ("enum".equals(viewDataVo.getType())) {
            dictTable = null;
        }
        return deviceModelMapper.getAttributeById(modelId, column, dictTable, dictKey, dictValue, dateformat);
    }

    @Override
    public List<ModelFunctionVo> getFunctionParams(String modelId) {
        return deviceModelMapper.selectFunctionsByModelId(modelId);
    }

    @Override
    public ModelFunctionVo getFunctionParam(String instanceId) {
        ModelFunctionVo functionVo = deviceModelMapper.selectFunctionById(instanceId);
        List<FunctionParamVo> paramVos = functionVo.getInputParams()
                .stream()
                .sorted(Comparator.comparing(FunctionParamVo::getSort))
                .collect(Collectors.toList());
        functionVo.setInputParams(
                paramVos
        );
        return functionVo;
    }
}
